using System;
using System.Text.RegularExpressions;
using data.contracts;
using NUnit.Framework;

namespace generator.tests
{
    [TestFixture]
    public class ClassGeneratorTests
    {
        [Test]
        public void Generate_board_output_with_type() {
            var pinDef = ClassGenerator.GenerateBoardOutput("MyOutput", "int");
            Assert.IsTrue(Regex.Match(pinDef, @"Action<int>\s+MyOutput").Success);
        }

        [Test]
        public void Generate_board_output_with_no_type() {
            var pinDef = ClassGenerator.GenerateBoardOutput("MyOutput", "");
            Assert.IsTrue(Regex.Match(pinDef, @"Action\s+MyOutput").Success);
        }

        [Test]
        public void Generate_board_input_with_no_type() {
            var pinDef = ClassGenerator.GenerateBoardInput("MyInput", "");
            Console.WriteLine(pinDef);
            Assert.IsTrue(Regex.Match(pinDef, @"Action\s+_MyInput.+\n.+void MyInput\(\)", RegexOptions.Multiline).Success);
        }

        [Test]
        public void Special_names_are_omitted_from_ctor_parameters() {
            Assert.That(ClassGenerator.GenerateParameters(new[] {
                new Pin {Board = "foo"},
                new Pin {Board = "$split1"}
            }), Is.EqualTo("IFoo foo"));
        }

        [Test]
        public void Namespace_in_ctor_parameters() {
            Assert.That(ClassGenerator.GenerateParameters(new[] {
                new Pin {Board = "n1.n2.foo"}
            }), Is.EqualTo("n1.n2.IFoo foo"));
        }

        [Test]
        public void this_is_omitted_from_ctor_parameters() {
            Assert.That(ClassGenerator.GenerateParameters(new[] {
                new Pin {Board = "foo"},
                new Pin {Board = "this"}
            }), Is.EqualTo("IFoo foo"));
        }

        [Test]
        public void Split_is_instanciated() {
            Assert.That(ClassGenerator.GenerateParts(new[] {
                new Wire("a.Out", "Tuple<int,string>", "(ISplit<Tuple<int,string>,int,string>) $split1.Input"),
                new Wire("(ISplit<Tuple<int,string>,int,string>) $split1.Output1", "int", "b.Input"),
                new Wire("(ISplit<Tuple<int,string>,int,string>) $split1.Output2", "string", "c.Input")
            }), Is.EqualTo("  var __split1 = new Split<Tuple<int,string>,int,string>();"));
        }

        [Test]
        public void Split_with_namespace_is_instanciated() {
            Assert.That(ClassGenerator.GenerateParts(new[] {
                new Wire("a.Out", "Tuple<int,string>", "(ebcbauteile.ISplit<Tuple<int,string>,int,string>) $split1.Input"),
                new Wire("(ebcbauteile.ISplit<Tuple<int,string>,int,string>) $split1.Output1", "int", "b.Input"),
                new Wire("(ebcbauteile.ISplit<Tuple<int,string>,int,string>) $split1.Output2", "string", "c.Input")
            }), Is.EqualTo("  var __split1 = new ebcbauteile.Split<Tuple<int,string>,int,string>();"));
        }

        [Test]
        public void Split_with_star_types_is_instanciated() {
            Assert.That(ClassGenerator.GenerateParts(new[] {
                new Wire("a.Out", "Tuple<int*,string>", "(ISplit<Tuple<int*,string>,int*,string>) $split1.Input"),
                new Wire("(ISplit<Tuple<int*,string>,int*,string>) $split1.Output1", "int*", "b.Input"),
                new Wire("(ISplit<Tuple<int*,string>,int*,string>) $split1.Output2", "string", "c.Input")
            }), Is.EqualTo("  var __split1 = new Split<Tuple<IEnumerable<int>,string>,IEnumerable<int>,string>();"));
        }

        [Test]
        public void Qualified_Pin_name_for_normal_variable() {
            Assert.That(ClassGenerator.QualifiedPinName(new Pin {Board = "a", Name = "in"}, BoardSyntax.Direction.Input), Is.EqualTo("a.in"));
        }

        [Test]
        public void Qualified_Pin_name_for_typed_variable() {
            Assert.That(ClassGenerator.QualifiedPinName(new Pin {Board = "a", Type = "(IFoo<int,string>)", Name = "in"}, BoardSyntax.Direction.Input),
                Is.EqualTo("a.in"));
        }

        [Test]
        public void Qualified_Pin_name_for_star_typed_variable() {
            Assert.That(ClassGenerator.QualifiedPinName(new Pin {Board = "a", Type = "(IFoo<int*,string>)", Name = "in"}, BoardSyntax.Direction.Input),
                Is.EqualTo("a.in"));
        }

        [Test]
        public void Qualified_Pin_name_for_special_variable() {
            Assert.That(ClassGenerator.QualifiedPinName(new Pin {Board = "$join1", Name = "Input"}, BoardSyntax.Direction.Input), Is.EqualTo("__join1.Input"));
        }

        [Test]
        public void Qualified_Pin_name_for_special_typed_variable() {
            Assert.That(
                ClassGenerator.QualifiedPinName(new Pin {Board = "$join7", Name = "Output1", Type = "(IFoo<int,string>)"}, BoardSyntax.Direction.Input),
                Is.EqualTo("__join7.Output1"));
        }

        [Test]
        public void Qualified_Pin_name_for_default_input() {
            Assert.That(ClassGenerator.QualifiedPinName(new Pin {Board = "part"}, BoardSyntax.Direction.Input), Is.EqualTo("part.Process"));
        }

        [Test]
        public void Qualified_Pin_name_for_default_output() {
            Assert.That(ClassGenerator.QualifiedPinName(new Pin {Board = "part"}, BoardSyntax.Direction.Output), Is.EqualTo("part.Result"));
        }

        [Test]
        public void Qualified_Pin_name_for_this_default_input() {
            Assert.That(ClassGenerator.QualifiedPinName(new Pin {Board = "this"}, BoardSyntax.Direction.Input), Is.EqualTo("this.Process"));
        }

        [Test]
        public void Qualified_Pin_name_for_this_default_output() {
            Assert.That(ClassGenerator.QualifiedPinName(new Pin {Board = "this"}, BoardSyntax.Direction.Output), Is.EqualTo("this.Result"));
        }

        [Test]
        public void Board_input_attached_to_two_wires() {
            var board = new Board("", "");
            board.Add(new Wire("this.Input", "t", "in1") {
                FromPin = new Pin {Board = "this", Name = "Input"},
                ToPin = new Pin {Board = "in1"}
            });
            board.Add(new Wire("this.Input", "t", "in2") {
                FromPin = new Pin {Board = "this", Name = "Input"},
                ToPin = new Pin {Board = "in2"}
            });

            var result = ClassGenerator.GenerateBoardInputs(board);

            Assert.That(result, Is.EqualTo("  private Action<t> _Input;\r\n  public void Input(t message) {\r\n    _Input(message);\r\n  }"));
        }

        [Test]
        public void Board_input_with_star_type() {
            var board = new Board("", "");
            board.Add(new Wire("this.Input", "t*", "in1") {
                FromPin = new Pin {Board = "this", Name = "Input"},
                ToPin = new Pin {Board = "in1"}
            });

            var result = ClassGenerator.GenerateBoardInputs(board);

            Assert.That(result,
                Is.EqualTo("  private Action<IEnumerable<t>> _Input;\r\n  public void Input(IEnumerable<t> message) {\r\n    _Input(message);\r\n  }"));
        }

        [Test]
        public void Board_output_attached_to_two_wires() {
            var board = new Board("", "");
            board.Add(new Wire("x.out1", "t", "this.Output") {
                FromPin = new Pin {Board = "x", Name = "out1"},
                ToPin = new Pin {Board = "this", Name = "Output"}
            });
            board.Add(new Wire("y.out2", "t", "this.Output") {
                FromPin = new Pin {Board = "y", Name = "out2"},
                ToPin = new Pin {Board = "this", Name = "Output"}
            });

            var result = ClassGenerator.GenerateBoardOutputs(board);

            Assert.That(result, Is.EqualTo("  public event Action<t> Output;\r\n"));
        }

        [Test]
        public void Board_output_with_star_type() {
            var board = new Board("", "");
            board.Add(new Wire("x.out1", "t*", "this.Output") {
                FromPin = new Pin {Board = "x", Name = "out1"},
                ToPin = new Pin {Board = "this", Name = "Output"}
            });

            var result = ClassGenerator.GenerateBoardOutputs(board);

            Assert.That(result, Is.EqualTo("  public event Action<IEnumerable<t>> Output;\r\n"));
        }

        [Test]
        public void To_this_with_parameter() {
            var wire = new Wire("x", "t", "this") {
                FromPin = new Pin {Board = "x"},
                ToPin = new Pin {Board = "this"}
            };
            Assert.That(ClassGenerator.GenerateWiring(wire), Is.EqualTo(
                "    x.Result += m => this.Result(m);\r\n"));
        }

        [Test]
        public void To_this_without_parameter() {
            var wire = new Wire("x", "", "this") {
                FromPin = new Pin {Board = "x"},
                ToPin = new Pin {Board = "this"}
            };
            Assert.That(ClassGenerator.GenerateWiring(wire), Is.EqualTo(
                "    x.Result += () => this.Result();\r\n"));
        }
    }
}